﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Dtd.Schema;
using System.Xml.XPath;
using System.Xml;
using System.Text.RegularExpressions;
using System.Diagnostics;
using Engine.Sgml.XPath;

namespace Engine.Sgml {
    /// <summary>
    /// Contains extension methods for SGML element.
    /// </summary>
    [DebuggerNonUserCode]
    public static class SElementExtension {
        /// <summary>
        /// Returns a collection of attributes of this element.
        /// </summary>
        /// <param name="element">A SElement object.</param>
        /// <returns>An IEnumerable&lt;SAttribute&gt; of attributes of this element.</returns>
        /// <exception cref="System.ArgumentNullException">If an <paramref name="element"/> is null.</exception>
        public static IEnumerable<SAttribute> Attributes(this SElement element) {
            Guard.ArgumentNotNull(element, "element");
            for (int i = 0; i < element.Attributes.Count; i++) {
                yield return element.Attributes[i];
                }
            }

        /// <summary>
        /// Returns a filtered collection of attributes of this element. Only elements that have
        /// a matching name are included in the collection.
        /// </summary>
        /// <param name="element">A SElement object.</param>
        /// <param name="name">The SName to match.</param>
        /// <returns>An IEnumerable&lt;SAttribute&gt; that contains the attributes of this element. Only elements that have a matching <paramref name="name"/> are included in the collection.</returns>
        /// <exception cref="System.ArgumentNullException">If an <paramref name="element"/> is null.</exception>
        public static IEnumerable<SAttribute> Attributes(this SElement element, SName name) {
            Guard.ArgumentNotNull(element, "element");
            for (int i = 0; i < element.Attributes.Count; i++) {
                if (element.Attributes[i].Name == name)
                    yield return element.Attributes[i];
                }
            }

        /// <summary>
        /// Returns the attribute for this element that has the specified name.
        /// </summary>
        /// <param name="element">A SElement object.</param>
        /// <param name="name">The name of the SAttribute to get.</param>
        /// <returns>A SAttribute that have a matching <paramref name="name"/>.</returns>
        public static SAttribute Attribute(this SElement element, string name) {
            Guard.ArgumentNotNull(element, "element");
            return element.Attributes[name];
            }

        internal static DContentModelType ContentModelType(this SElement element) {
            if (element.Declaration == null)
                return DContentModelType.Default;
            else
                return element.Declaration.ContentModel.ContentType;
            }

        internal static bool EndTagOptional(this SElement element) {
            if (element.Declaration == null)
                return false;
            else
                return element.Declaration.EndTagOptional;
            }

        internal static void MoveToAncestor(this SElement element, SElement parent) {
            Debug.Assert(element != null);
            Debug.Assert(parent != null);
            if (element.Parent != null)
                element.Parent.Nodes.RemoveAt(element.OrderIndex);
            parent.Nodes.Add(element);
            element.Parent = parent;
            }

        internal static SElement NextNonInclusionNode(this SElement element) {
            SElement next = element.NextNode;
            while (next != null && next.DName == InclusionNode.Instance.Name) {
                next = next.NextNode;
                }
            return next;
            }

        internal static SElement PreviousNonInclusionNode(this SElement element) {
            SElement previous = element.PreviousNode;
            while (previous != null && previous.DName == InclusionNode.Instance.Name) {
                previous = previous.PreviousNode;
                }
            return previous;
            }

        internal static SElement FirstNonInclusionNode(this SElement element) {
            SElement first = element.Nodes.FirstElement;
            if (first == null)
                return null;
            else if (first.DName == InclusionNode.Instance.Name)
                return first.NextNonInclusionNode();
            else
                return first;
            }

        internal static SElement LastNonInclusionNode(this SElement element) {
            SElement last = element.Nodes.LastElement;
            if (last == null)
                return null;
            else if (last.DName == InclusionNode.Instance.Name)
                return last.PreviousNonInclusionNode();
            else
                return last;
            }

        internal static bool IsXmlDeclaration(this SElement element) {
            return element.NodeType == SgmlNodeType.ProcessingInstruction &&
                string.Equals(element.LocalName, "xml", StringComparison.OrdinalIgnoreCase);
            }
        }
    }
